package com.aphrodite.io.sip.transaction;

import com.aphrodite.io.sip.message.Message;
import com.aphrodite.io.sip.message.SipMethods;
import com.aphrodite.io.sip.provider.ConnectionIdentifier;
import com.aphrodite.io.sip.provider.SipProvider;
import com.aphrodite.io.sip.provider.SipStack;
import com.aphrodite.io.sip.provider.TransactionIdentifier;
import com.aphrodite.io.sip.tools.Timer;

/**
 * ACK server transaction should follow an INVITE server transaction within an
 * INVITE Dialog in a SIP UAC. The AckTransactionServer sends the final response
 * message and retransmits it several times until the method terminate() is
 * called or the trasaction timeout fires.
 */
public class AckTransactionServer extends Transaction {
	/** last response message */
	Message response;

	/** retransmission timeout */
	Timer retransmission_to;

	/**
	 * the TransactionServerListener that captures the events fired by the
	 * AckTransactionServer
	 */
	AckTransactionServerListener transaction_listener;
	/** transaction timeout */
	Timer transaction_to;

	/**
	 * Creates a new AckTransactionServer. The AckTransactionServer starts
	 * sending the response message <i>resp</i> through the connection
	 * <i>conn_id</i>.
	 */
	public AckTransactionServer(SipProvider sip_provider, ConnectionIdentifier connection_id, Message resp,
			AckTransactionServerListener listener) {
		super(sip_provider);
		response = resp;
		init(listener, new TransactionIdentifier(SipMethods.ACK), connection_id);
	}

	/** Initializes timeouts and state */
	/**
	 * Creates a new AckTransactionServer. The AckTransactionServer starts
	 * sending a the <i>resp</i> message. It retransmits the resp several times
	 * if no ACK request is received.
	 */
	public AckTransactionServer(SipProvider sip_provider, Message resp, AckTransactionServerListener listener) {
		super(sip_provider);
		response = resp;
		init(listener, new TransactionIdentifier(SipMethods.ACK), null);
	}

	/** Initializes timeouts and listener. */
	void init(AckTransactionServerListener listener, TransactionIdentifier transaction_id,
			ConnectionIdentifier connection_id) {
		this.transaction_listener = listener;
		this.transaction_id = transaction_id;
		this.connection_id = connection_id;
		transaction_to = new Timer(SipStack.transaction_timeout, "Transaction", this);
		retransmission_to = new Timer(SipStack.retransmission_timeout, "Retransmission", this);
		// (CHANGE-040905) now timeouts are started when method respond() is
		// called
		// transaction_to.start();
		// if (connection_id==null) retransmission_to.start();
		log.info(this, "id: " + String.valueOf(transaction_id));
		log.info(this, "created");
	}

	/**
	 * Method derived from interface TimerListener. It's fired from an active
	 * Timer.
	 */
	public void onTimeout(Timer to) {
		try {
			if (to.equals(retransmission_to) && statusIs(STATE_PROCEEDING)) {
				log.info(this, "Retransmission timeout expired");
				long timeout = 2 * retransmission_to.getTime();
				if (timeout > SipStack.max_retransmission_timeout)
					timeout = SipStack.max_retransmission_timeout;
				retransmission_to = new Timer(timeout, retransmission_to.getLabel(), this);
				retransmission_to.start();
				sip_provider.sendMessage(response, connection_id);
			}
			if (to.equals(transaction_to) && statusIs(STATE_PROCEEDING)) {
				log.info(this, "Transaction timeout expired");
				changeStatus(STATE_TERMINATED);
				if (transaction_listener != null)
					transaction_listener.onTransAckTimeout(this);
				// (CHANGE-040421) now it can free links to transaction_listener
				// and timers
				transaction_listener = null;
				// retransmission_to=null;
				// transaction_to=null;
			}
		} catch (Exception e) {
			log.error(this, "onTimeout", e);
		}
	}

	/** Starts the AckTransactionServer. */
	public void respond() {
		log.info(this, "start");
		changeStatus(STATE_PROCEEDING);
		// transaction_id=null; // it is not required since no
		// SipProviderListener is implemented
		// (CHANGE-040905) now timeouts are started when method respond() is
		// called
		transaction_to.start();
		if (connection_id == null)
			retransmission_to.start();

		sip_provider.sendMessage(response, connection_id);
	}

	/** Method used to drop an active transaction. */
	public void terminate() {
		retransmission_to.halt();
		transaction_to.halt();
		changeStatus(STATE_TERMINATED);
		// if (transaction_listener!=null)
		// transaction_listener.onAckSrvTerminated(this);
		// (CHANGE-040421) now it can free links to transaction_listener and
		// timers
		transaction_listener = null;
		// retransmission_to=null;
		// transaction_to=null;
	}

}
